001 /* 002 * Copyright 2003-2005 The Apache Software Foundation 003 * Copyright 2005 Stephen McConnell 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package net.dpml.cli.option; 018 019 import java.util.ArrayList; 020 import java.util.Comparator; 021 import java.util.Iterator; 022 import java.util.List; 023 import java.util.Set; 024 025 import net.dpml.cli.Argument; 026 import net.dpml.cli.Option; 027 import net.dpml.cli.OptionException; 028 import net.dpml.cli.WriteableCommandLine; 029 import net.dpml.cli.resource.ResourceConstants; 030 import net.dpml.cli.resource.ResourceHelper; 031 032 /** 033 * An Argument implementation that allows a variable size Argument to precede a 034 * fixed size argument. The canonical example of it's use is in the unix 035 * <code>cp</code> command where a number of source can be specified with 036 * exactly one destination specfied at the end. 037 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a> 038 * @version 1.0.0 039 */ 040 public class SourceDestArgument extends ArgumentImpl 041 { 042 private final Argument m_source; 043 private final Argument m_dest; 044 045 /** 046 * Creates a SourceDestArgument using defaults where possible. 047 * 048 * @param source the variable size Argument 049 * @param dest the fixed size Argument 050 */ 051 public SourceDestArgument( 052 final Argument source, final Argument dest ) 053 { 054 this( 055 source, 056 dest, 057 DEFAULT_INITIAL_SEPARATOR, 058 DEFAULT_SUBSEQUENT_SEPARATOR, 059 DEFAULT_CONSUME_REMAINING, 060 null ); 061 } 062 063 /** 064 * Creates a SourceDestArgument using the specified parameters. 065 * 066 * @param source the variable size Argument 067 * @param dest the fixed size Argument 068 * @param initialSeparator the inistial separator to use 069 * @param subsequentSeparator the subsequent separator to use 070 * @param consumeRemaining the token triggering consume remaining behaviour 071 * @param defaultValues the default values for the SourceDestArgument 072 */ 073 public SourceDestArgument( 074 final Argument source, final Argument dest, final char initialSeparator, 075 final char subsequentSeparator, final String consumeRemaining, 076 final List defaultValues ) 077 { 078 super( 079 "SourceDestArgument", null, sum( source.getMinimum(), dest.getMinimum() ), 080 sum( source.getMaximum(), dest.getMaximum() ), initialSeparator, 081 subsequentSeparator, null, consumeRemaining, defaultValues, 0 ); 082 083 m_source = source; 084 m_dest = dest; 085 086 if( dest.getMinimum() != dest.getMaximum() ) 087 { 088 throw new IllegalArgumentException( 089 ResourceHelper.getResourceHelper().getMessage( 090 ResourceConstants.SOURCE_DEST_MUST_ENFORCE_VALUES ) ); 091 } 092 } 093 094 private static int sum( final int a, final int b ) 095 { 096 return Math.max( a, Math.max( b, a + b ) ); 097 } 098 099 /** 100 * Appends usage information to the specified StringBuffer 101 * 102 * @param buffer the buffer to append to 103 * @param helpSettings a set of display settings @see DisplaySetting 104 * @param comp a comparator used to sort the Options 105 */ 106 public void appendUsage( 107 final StringBuffer buffer, final Set helpSettings, final Comparator comp ) 108 { 109 final int length = buffer.length(); 110 111 m_source.appendUsage( buffer, helpSettings, comp ); 112 113 if( buffer.length() != length ) 114 { 115 buffer.append( ' ' ); 116 } 117 118 m_dest.appendUsage( buffer, helpSettings, comp ); 119 } 120 121 /** 122 * Builds up a list of HelpLineImpl instances to be presented by HelpFormatter. 123 * 124 * @see net.dpml.cli.HelpLine 125 * @see net.dpml.cli.util.HelpFormatter 126 * @param depth the initial indent depth 127 * @param helpSettings the HelpSettings that should be applied 128 * @param comp a comparator used to sort options when applicable. 129 * @return a List of HelpLineImpl objects 130 */ 131 public List helpLines( 132 int depth, Set helpSettings, Comparator comp ) 133 { 134 final List helpLines = new ArrayList(); 135 helpLines.addAll( m_source.helpLines( depth, helpSettings, comp ) ); 136 helpLines.addAll( m_dest.helpLines( depth, helpSettings, comp ) ); 137 return helpLines; 138 } 139 140 /** 141 * Checks that the supplied CommandLine is valid with respect to the 142 * suppled option. 143 * 144 * @param commandLine the CommandLine to check. 145 * @param option the option to evaluate 146 * @throws OptionException if the CommandLine is not valid. 147 */ 148 public void validate( WriteableCommandLine commandLine, Option option ) 149 throws OptionException 150 { 151 final List values = commandLine.getValues( option ); 152 153 final int limit = values.size() - m_dest.getMinimum(); 154 int count = 0; 155 156 final Iterator i = values.iterator(); 157 158 while( count++ < limit ) 159 { 160 commandLine.addValue( m_source, i.next() ); 161 } 162 163 while( i.hasNext() ) 164 { 165 commandLine.addValue( m_dest, i.next() ); 166 } 167 168 m_source.validate( commandLine, m_source ); 169 m_dest.validate( commandLine, m_dest ); 170 } 171 172 /** 173 * Indicates whether this Option will be able to process the particular 174 * argument. 175 * 176 * @param commandLine the CommandLine object to store defaults in 177 * @param arg the argument to be tested 178 * @return true if the argument can be processed by this Option 179 */ 180 public boolean canProcess( 181 final WriteableCommandLine commandLine, final String arg ) 182 { 183 return m_source.canProcess( commandLine, arg ) || m_dest.canProcess( commandLine, arg ); 184 } 185 }